/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

/*
 * Testing3D.java
 *
 * Created on 27/05/2009, 11:10:16
 */
package gui.graphs3d;

import com.sun.j3d.utils.behaviors.vp.OrbitBehavior;
import com.sun.j3d.utils.geometry.*;
import com.sun.j3d.utils.universe.SimpleUniverse;
import com.sun.j3d.utils.universe.ViewingPlatform;
import data.ConfigurationGraphData;
import data.ConfigurationGraphObject;
import data.GraphData;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.GraphicsConfiguration;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.util.Collections;
import java.util.Comparator;
import java.util.Vector;
import javax.media.j3d.AmbientLight;
import javax.media.j3d.Appearance;
import javax.media.j3d.Background;
import javax.media.j3d.BoundingSphere;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.Canvas3D;
import javax.media.j3d.DirectionalLight;
import javax.media.j3d.Font3D;
import javax.media.j3d.FontExtrusion;
import javax.media.j3d.Material;
import javax.media.j3d.Shape3D;
import javax.media.j3d.Text3D;
import javax.media.j3d.Transform3D;
import javax.media.j3d.TransformGroup;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.vecmath.Color3f;
import javax.vecmath.Point3d;
import javax.vecmath.Point3f;
import javax.vecmath.Vector3d;
import javax.vecmath.Vector3f;

import main.ToEngineFacade;

/**
 *
 * @author Elad
 */
public abstract class Graph3D extends javax.swing.JPanel {


	private BoundingSphere bounds;
	private SimpleUniverse simpleU;
	private Canvas3D canvas3D;
	private JPanel graphViewPanel;
	private JTextArea msgTextArea;
	private JScrollPane msgScrollPane;
	private TransformGroup objSpin;
	//    protected ConfigurationGraphData gData;
	private final float PointRadius = 0.01f;
	private BranchGroup objRoot;
	private BranchGroup scene;
	private ViewingPlatform vp;
	private TransformGroup objMove;

	protected boolean j3dInstalled = true;

	/** Creates new form Testing3D */
	public Graph3D() {
		try{
			initComponents();
			// this.gData = gData;
			//this.setState(this.MAXIMIZED_BOTH);

			//this.setSize(900, 900);
			//        this.setExtendedState(JFrame.MAXIMIZED_BOTH);
			msgTextArea = new JTextArea("Click on a point for it's details \n", 6, 3);
			msgTextArea.setFont(new Font("Helvetica", Font.PLAIN, 15));
			msgScrollPane = new JScrollPane(msgTextArea);            
			msgTextArea.setEditable(false);
			graphViewPanel = new JPanel(new BorderLayout());

			bounds = new BoundingSphere(new Point3d(0, 0, 0), 10);

			setLayout(new BorderLayout());
			GraphicsConfiguration config =
				SimpleUniverse.getPreferredConfiguration();
			canvas3D = new Canvas3D(config);
			simpleU = new SimpleUniverse(canvas3D);
			vp = simpleU.getViewingPlatform();
			vp.setNominalViewingTransform();

			scene = createSceneGraph();
			// SimpleUniverse is a Convenience Utility class

			graphViewPanel.add(canvas3D, BorderLayout.CENTER);
			graphViewPanel.add(msgScrollPane, BorderLayout.SOUTH);
			//  graphViewPanel.add(saveButton, BorderLayout.SOUTH);
			this.add(graphViewPanel, BorderLayout.CENTER);
		}catch (UnsatisfiedLinkError e) {
			JOptionPane.showMessageDialog(this, "Java3D is not installed, to see the graph ouput, please install Java3D", "Error", JOptionPane.ERROR_MESSAGE);
			j3dInstalled = false;
		}catch (NoClassDefFoundError e) {
			JOptionPane.showMessageDialog(this, "Java3D is not installed, to see the graph ouput, please install Java3D", "Error", JOptionPane.ERROR_MESSAGE);
			j3dInstalled = false;
		}

		// this.add( canvas3D,BorderLayout.CENTER);
		//         graphViewPanel.add(canvas3D);


	}

	protected void addAxises(Shape3D axisesShape) {
		TransformGroup axises = new TransformGroup();
		axises.addChild(axisesShape);
		axises.setPickable(false);
		objSpin.addChild(axises);
	}

	protected abstract void addGraphPoints();

	protected void addRotationBehavior(Vector<TransformGroup> texts) {
		//adding rotation Behavior
		keyRotationBehavior rotateBehavior = new keyRotationBehavior(objSpin, texts);
		rotateBehavior.setSchedulingBounds(bounds);
		objMove.addChild(rotateBehavior);
	}

	/**
	 *  Must activate at the end of building brachgroup
	 * @param objRoot
	 */
	protected void finishGraphCreation() {
		objRoot.compile();
		// This will move the ViewPlatform back a bit so the
		// objects in the scene can be viewed.       
		//setOrbitBehavior();
		simpleU.addBranchGraph(scene);
	}

	protected void setOrbitBehavior() {
		OrbitBehavior B = new OrbitBehavior(canvas3D, OrbitBehavior.REVERSE_TRANSLATE | OrbitBehavior.DISABLE_ROTATE);
		B.setSchedulingBounds(bounds);
		vp.setViewPlatformBehavior(B); //   simpleU.getViewer().getView().setViewPolicy(View.VIRTUAL_WORLD);        
	}

	private Background createBackground() {
		//  String texturePath = "file://localhost/" + System.getProperty("user.dir") + "/stone.jpg";
		// TextureLoader backgroundTexture = new TextureLoader(new java.net.URL(texturePath), null);
		//     new TextureLoader(backgroundImage, this);
		Background background = new Background(new Color3f(new Color(0, 0, 0)));
		//Background background = new Background(new Color3f(new Color(255, 165, 79)));

		// Background background = new Background(backgroundTexture.getImage());
		background.setApplicationBounds(bounds);
		return background;
	}

	protected abstract void setGraphLocation(Transform3D t); 


	private BranchGroup createSceneGraph() {
		// Create the root of the branch graph
		Transform3D systemLocation = new Transform3D();
		systemLocation.set(new Vector3f(0.0f, 0.0f, -0.5f));
		Transform3D t3D = new Transform3D();
		setGraphLocation(t3D);

		objMove = new TransformGroup(t3D);
		objRoot = new BranchGroup();
		objRoot.addChild(objMove);
		objSpin = new TransformGroup(systemLocation);
		objSpin.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
		objMove.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);

		objMove.addChild(objSpin);
		// addGraphPoints();
		//        addAxises(new PositiveAxis());
		//  objRoot.addChild(axises);

		//rotating graph
		/*Transform3D yAxis = new Transform3D();
        yAxis.set(new Vector3f(0.0f, 0.0f, 0.0f));
        //yAxis.rotY(Math.PI / 4.0d);
        // Alpha (num of loops , time in millisec for one loop)
        Alpha rotationAlpha = new Alpha(-1, 16000);

        RotationInterpolator rotator =
        new RotationInterpolator(rotationAlpha, objSpin, yAxis,
        0.0f, (float) Math.PI * 2.0f);

        rotator.setSchedulingBounds(bounds);
        objSpin.addChild(rotator);*/
		objRoot.addChild(createBackground());

		//adding lighting    
		Color3f alColour = new Color3f(0.4f, 0.4f, 0.4f);
		AmbientLight aLgt = new AmbientLight(alColour);
		aLgt.setInfluencingBounds(bounds);
		objMove.addChild(aLgt);

		DirectionalLight lightD = new DirectionalLight();
		lightD.setInfluencingBounds(bounds);
		lightD.setDirection(new Vector3f(0.0f, 0.0f, -1.0f));
		lightD.setColor(new Color3f(0.6f, 0.6f, 0.6f));
		objMove.addChild(lightD);


		/*Vector<TransformGroup> texts = new Vector();
        texts.add(addText(objSpin, "power", 1.11, 0.0, 0.0, 0.05, 0.05, 0.05));
        texts.add(addText(objSpin, "price", 0.01, 0, 1.11, 0.05, 0.05, 0.05));
        texts.add(addText(objSpin, "performance", 0.0, 1.02, 0.0, 0.05, 0.05, 0.05));*/

		//ading picking behavior
		MousePickShapeBehavior pickPointsBehavior = new MousePickShapeBehavior(canvas3D, msgTextArea, objRoot, bounds);
		objRoot.addChild(pickPointsBehavior);

		/*KeyNavigatorBehavior keyNavBeh = new KeyNavigatorBehavior(objSpin ) ;
        keyNavBeh.setSchedulingBounds(bounds);
        objRoot.addChild(keyNavBeh);*/

		//finishGraphCreation(objRoot);

		return objRoot;
	} // end of CreateSceneGraph method of HelloJava3Da

	/**
	 * adding points to positive axises
	 * @param objSpin the group the sphere will be added to
	 * @param x between 0-1
	 * @param y between 0-1
	 * @param z between 0-1
	 */
	protected void addPoint(float x, float y, float z, String msg) {
		Transform3D translate = new Transform3D();
		translate.set(new Vector3f(x, y, z)); //location of sphere
		Appearance sphAppe = new Appearance();
		Color3f blue = new Color3f(new Color(26, 175, 164));//color of sphere
		Color3f black = new Color3f(0.0f, 0.0f, 0.0f);
		Color3f lightBlue = new Color3f(new Color(175, 238, 238));
		sphAppe.setMaterial(new Material(blue, black, blue, blue, 80.0f));
		Sphere sphere = new Sphere(PointRadius, sphAppe);
		TransformGroup objLocation = new TransformGroup(translate);
		Shape3D shape = new Shape3D(sphere.getShape().getGeometry(), sphAppe);
		shape.setCapability(Shape3D.ALLOW_APPEARANCE_WRITE);
		shape.setCapability(Shape3D.ALLOW_APPEARANCE_READ);
		shape.setUserData(msg);
		objLocation.addChild(shape);
		objSpin.addChild(objLocation);
	}

	protected TransformGroup add3DText(String text, double xLoc, double yLoc, double zLoc, double xSize, double ySize, double zSize, Transform3D rotation) {
		Appearance textAppear = new Appearance();
		Color3f textColor = new Color3f(new Color(58, 95, 205));//(new Color(167, 66, 91));//color of text
		Color3f black = new Color3f(0.0f, 0.0f, 0.0f);
		Color3f lightBlue = new Color3f(new Color(175, 238, 238));
		textAppear.setMaterial(new Material(textColor, black, textColor, textColor, 80.0f));
		Font3D font3d = new Font3D(new Font("Helvetica", Font.PLAIN, 1), new FontExtrusion());
		Text3D textGeom = new Text3D(font3d, new String(text), new Point3f(-2.0f, 0.0f, 0.0f));
		Shape3D textShape = new Shape3D(textGeom);
		textShape.setAppearance(textAppear);
		Transform3D translate = new Transform3D();
		translate.set(new Vector3d(xLoc, yLoc, zLoc));
		translate.setScale(new Vector3d(xSize, ySize, zSize));
		if (rotation != null) {
			translate.mul(rotation);
		}
		TransformGroup textGroup = new TransformGroup();
		textGroup.setTransform(translate);
		textShape.setPickable(false);
		textGroup.addChild(textShape);
		objSpin.addChild(textGroup);
		return textGroup;
	}

	protected TransformGroup add3DText(String text, double xLoc, double yLoc, double zLoc, double xSize, double ySize, double zSize) {
		return add3DText(text, xLoc, yLoc, zLoc, xSize, ySize, zSize, null);
	}

	protected void makeBox(float width, float height, float depth) {
		float xDim = width / 2.0f;
		float yDim = height / 2.0f;
		float zDim = depth / 2.0f;
		/*Appearance app = new Appearance();
        // switch off face culling
        PolygonAttributes pa = new PolygonAttributes();
        pa.setCullFace(PolygonAttributes.CULL_BACK);
        app.setPolygonAttributes(pa);
        // semi-transparent appearance
        TransparencyAttributes ta = new TransparencyAttributes();
        ta.setTransparencyMode(TransparencyAttributes.BLENDED);
        ta.setTransparency(0.9f); // 1.0f is totally transparent
        app.setTransparencyAttributes(ta);*/
		// position the box: centered, sitting on the XZ plane
		Transform3D t3d = new Transform3D();
		t3d.set(new Vector3f(xDim, yDim, zDim));
		/* the box is a bit above the floor, so it doesn't visual
        interact with the floor. */
		TransformGroup boxTG = new TransformGroup(t3d);
		TransperantBox spaceOfPoints = new TransperantBox(xDim, yDim, zDim);
		/*Shape3D bottom = spaceOfPoints.getShape(Box.BOTTOM);
        Shape3D top = spaceOfPoints.getShape(Box.TOP);
        Shape3D back = spaceOfPoints.getShape(Box.BACK);
        Shape3D front = spaceOfPoints.getShape(Box.FRONT);
        Shape3D left = spaceOfPoints.getShape(Box.LEFT);
        Shape3D right = spaceOfPoints.getShape(Box.RIGHT);
        Appearance textAppear = new Appearance();
        Color3f blue = new Color3f(new Color(167, 66, 91));//color of text
        Color3f black = new Color3f(0.0f, 0.0f, 0.0f);
        Color3f lightBlue = new Color3f(new Color(175, 238, 238));
        textAppear.setMaterial(new Material(blue, black, blue, blue, 80.0f));
        bottom.setAppearance(textAppear);*/
		boxTG.addChild(spaceOfPoints);

		// set the box's dimensions and appearance
		/*Shape3D edgesShape = makeBoxEdges(width, height, depth); // edges
        edgesShape.setPickable(false);
        objSpin.addChild(edgesShape);*/
		// collect the box and edges together under a single BranchGroup
		boxTG.setPickable(false);
		objSpin.addChild(boxTG);

	} // end of makeBox()

	/*//creating lines around the edge of the box
    private Shape3D makeBoxEdges(float x, float y, float z) {
    int numOfPoints = 24;
    LineArray edges = new LineArray(numOfPoints, LineArray.COORDINATES |
    LineArray.COLOR_3);
    Point3f pts[] = new Point3f[numOfPoints];
    // front edges
    pts[0] = new Point3f(0, 0, z); // edge 1 (left)
    pts[1] = new Point3f(0, y, z);
    pts[2] = new Point3f(0, y, z); // edge 2 (top)
    pts[3] = new Point3f(x, y, z);
    pts[4] = new Point3f(x, y, z); // edge 3 (right)
    pts[5] = new Point3f(x, 0, z);
    // back edges
    pts[6] = new Point3f(0, 0, 0); // edge 4 (left)
    pts[7] = new Point3f(0, y, 0);
    pts[8] = new Point3f(0, y, 0); // edge 5 (top)
    pts[9] = new Point3f(x, y, 0);
    pts[10] = new Point3f(x, y, 0); // edge 6 (right)
    pts[11] = new Point3f(x, 0, 0);
    // top edges, running front to back
    pts[12] = new Point3f(0, y, z); // edge 7 (left)
    pts[13] = new Point3f(0, y, 0);
    pts[14] = new Point3f(x, y, z); // edge 8 (right)
    pts[15] = new Point3f(x, y, 0);

    pts[16] = new Point3f(0, 0, 0);
    pts[17] = new Point3f(0, 0, z);
    pts[18] = new Point3f(0, 0, 0);
    pts[19] = new Point3f(x, 0, 0);
    pts[20] = new Point3f(x, 0, 0);
    pts[21] = new Point3f(x, 0, z);
    pts[22] = new Point3f(x, 0, z);
    pts[23] = new Point3f(0, 0, z);
    edges.setCoordinates(0, pts);
    // set the edges colour to yellow
    for (int i = 0; i < pts.length; i++) {
    edges.setColor(i, new Color3f(new Color(3, 141, 187)));
    }
    Shape3D edgesShape = new Shape3D(edges);
    // make the edges (lines) thicker
    Appearance app = new Appearance();
    LineAttributes la = new LineAttributes();
    la.setLineWidth(1);
    app.setLineAttributes(la);
    edgesShape.setAppearance(app);
    return edgesShape;
    } // end of makeBoxEdges()*/
	/** This method is called from within the constructor to
	 * initialize the form.
	 * WARNING: Do NOT modify this code. The content of this method is
	 * always regenerated by the Form Editor.
	 */
	@SuppressWarnings("unchecked")
	// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
	private void initComponents() {

		//        setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);

		javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
		setLayout(layout);
		layout.setHorizontalGroup(
				layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
				.addGap(0, 400, Short.MAX_VALUE)
		);
		layout.setVerticalGroup(
				layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
				.addGap(0, 300, Short.MAX_VALUE)
		);

		//        pack();
	}// </editor-fold>//GEN-END:initComponents

	/**
	 *  transforming all x, y, z value to the range of 0 to 1
	 * adding String to describe the value of this point
	 * power is x
	 * performance is y
	 * price is z
	 * normilaization will be for each x, y, z:
	 * newX=(oldx-minX)/(maxX-minX)
	 * this will make all points to be distibuted between 0 to 1
	 * @param points
	 * @return
	 */
	protected Vector<ConfigurationGraphObject> sortPoints(ConfigurationGraphData gData) {
		return gData.get_ppp();
	}

	/**
	 * if there several configurations on the same point unite those configurations to one point
	 * @param pointValues
	 * @return
	 */
	protected Vector<PointValues> unitepoints(Vector<PointValues> pointValues) {
		Vector<PointValues> unitedPoints = new Vector<PointValues>();
		PointValues tPoint;
		Vector<PointValues> pointsSortedByX = (Vector<PointValues>) pointValues.clone();
		Collections.sort(pointsSortedByX, new Comparator<PointValues>() {

			@Override
			public int compare(PointValues o1, PointValues o2) {
				float diff = o1.getX() - o2.getX();
				if (diff > 0) {
					return 1;
				}
				if (diff < 0) {
					return -1;
				}
				return 0;
			}
		});
		//     System.out.println("sorted points: "+pointsSortedByX);
		tPoint = pointsSortedByX.get(0);
		for (int i = 1; i < pointsSortedByX.size(); i++) {
			if (tPoint.near(pointsSortedByX.get(i))) {
				tPoint.unite(pointsSortedByX.get(i));
			} else {
				unitedPoints.add(tPoint);
				tPoint = pointsSortedByX.get(i);
			}
		}
		unitedPoints.add(tPoint);
		//       System.out.println("united Points: "+unitedPoints);
		return unitedPoints;

	}

	protected class PointValues {

		private float x,  y,  z;
		private String msg;

		public PointValues(float x, float y, float z, String msg) {
			this.x = x;
			this.y = y;
			this.z = z;
			this.msg = msg;
		}

		public String getMsg() {
			return msg;
		}

		public float getX() {
			return x;
		}

		public float getY() {
			return y;
		}

		public float getZ() {
			return z;
		}

		private boolean near(PointValues other) {
			float threshold = PointRadius;
			if (Math.abs(x - other.getX()) > threshold) {
				return false;
			}
			if (Math.abs(y - other.getY()) > threshold) {
				return false;
			}
			if (Math.abs(z - other.getZ()) > threshold) {
				return false;
			}
			return true;
		}

		private void unite(PointValues other) {
			this.msg += "\n\n" + other.getMsg();
		}

		@Override
		public String toString() {
			return "x: " + x + " y: " + y + " z: " + z;
		}

        @Override
        public boolean equals(Object obj) {
            if (obj == null) {
                return false;
            }
            if (getClass() != obj.getClass()) {
                return false;
            }
            final PointValues other = (PointValues) obj;
            if (this.x != other.x) {
                return false;
            }
            if (this.y != other.y) {
                return false;
            }
            if (this.z != other.z) {
                return false;
            }
            return true;
        }

        @Override
        public int hashCode() {
            int hash = 5;
            hash = 37 * hash + Float.floatToIntBits(this.x);
            hash = 37 * hash + Float.floatToIntBits(this.y);
            hash = 37 * hash + Float.floatToIntBits(this.z);
            return hash;
        }

        
	}

	// Variables declaration - do not modify//GEN-BEGIN:variables
	// End of variables declaration//GEN-END:variables
}
